<template>
  <div class="s-canvas">
    <canvas
      id="s-canvas"
      :width="contentWidth"
      :height="contentHeight"
      @click="refreshCode"
    ></canvas>
  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted, withDefaults, defineProps } from "vue";

interface CodeProps {
  defaultCode?: string;
  contentWidth?: number;
  contentHeight?: number;
  maxLine?: number;
  maxDot?: number;
  fontSizeMin?: number;
  fontSizeMax?: number;
}

const props = withDefaults(defineProps<CodeProps>(), {
  contentWidth: 90,
  contentHeight: 30,
  fontSizeMin: 25,
  fontSizeMax: 30,
  maxLine: 4,
  maxDot: 10,
});

const emit = defineEmits(["update:verifyCode"]);
const verifyCode = ref("");

onMounted(() => {
  verifyCode.value = props.defaultCode || makeCode();
  emit("update:verifyCode", verifyCode.value);
  drawPic(verifyCode.value);
});

const makeCode = (len = 4) => {
  let code = "";
  const identifyCodes = "123456789abcdefjhijkinpqrsduvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
  for (let i = 0; i < len; i++) {
    code += identifyCodes[Math.floor(Math.random() * identifyCodes.length)];
  }
  return code;
};

const refreshCode = () => {
  verifyCode.value = makeCode();
  emit("update:verifyCode", verifyCode.value);
  drawPic(verifyCode.value);
};

const drawPic = (verifyCode: string) => {
  const canvas = document.getElementById("s-canvas") as HTMLCanvasElement;
  const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
  ctx.textBaseline = "bottom";
  ctx.fillStyle = "#e6ecfd";
  ctx.fillRect(0, 0, props.contentWidth, props.contentHeight);
  for (let i = 0; i < verifyCode.length; i++) {
    drawText(ctx, verifyCode, i);
  }
  drawLine(ctx, props.maxLine);
  drawDot(ctx, props.maxDot);
};

const drawText = (ctx: CanvasRenderingContext2D, verifyCode: string, index: number) => {
  ctx.fillStyle = `rgb(${Math.floor(Math.random() * 160)},${Math.floor(Math.random() * 160)},${Math.floor(Math.random() * 160)})`;
  ctx.font = `${Math.floor(Math.random() * (props.fontSizeMax - props.fontSizeMin) + props.fontSizeMin)}px SimHei`;
  const x = (index + 1) * (props.contentWidth / (verifyCode.length + 1));
  const y = Math.floor(Math.random() * (props.contentHeight - props.fontSizeMax)) + props.fontSizeMax;
  const deg = Math.floor(Math.random() * 30) - 15;
  ctx.translate(x, y);
  ctx.rotate((deg * Math.PI) / 180);
  ctx.fillText(verifyCode[index], 0, 0);
  ctx.rotate((-deg * Math.PI) / 180);
  ctx.translate(-x, -y);
};

const drawLine = (ctx: CanvasRenderingContext2D, maxLine: number) => {
  for (let i = 0; i < maxLine; i++) {
    ctx.strokeStyle = `rgb(${Math.floor(Math.random() * 200)},${Math.floor(Math.random() * 200)},${Math.floor(Math.random() * 200)})`;
    ctx.beginPath();
    ctx.moveTo(Math.floor(Math.random() * props.contentWidth), Math.floor(Math.random() * props.contentHeight));
    ctx.lineTo(Math.floor(Math.random() * props.contentWidth), Math.floor(Math.random() * props.contentHeight));
    ctx.stroke();
  }
};

const drawDot = (ctx: CanvasRenderingContext2D, maxDot: number) => {
  for (let i = 0; i < maxDot; i++) {
    ctx.fillStyle = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`;
    ctx.beginPath();
    ctx.arc(Math.floor(Math.random() * props.contentWidth), Math.floor(Math.random() * props.contentHeight), 1, 0, 2 * Math.PI);
    ctx.fill();
  }
};
</script>